package org.grapes.wine.api.server.method.handler;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.grapes.wine.api.server.database.ReadData;
import org.grapes.wine.api.server.exception.MethodErrorCode;
import org.grapes.wine.api.server.exception.MethodException;
import org.grapes.wine.api.server.method.command.MethodCommand;
import org.grapes.wine.api.server.util.SQLHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

public abstract class MethodHandler implements MethodCommand {
	/*
	 * no parameters here for security reason
	 */

	public Document init(HttpServletRequest request, List param,
			SQLHelper sqlHelper) throws Exception {
		// build a new Document
		Document doc = BuildNewDocument();

		try {
			// fetch data
			List<Map> dataList = initDataList(sqlHelper);

			doc = initDocument(request, doc, dataList);
		} catch (MethodException we) {
			doc = we.getDocument();
		}

		return doc;
	}

	private Document BuildNewDocument() {
		DocumentBuilderFactory documentBuilderFactory;
		documentBuilderFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder = null;
		try {
			docBuilder = documentBuilderFactory.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			try {
				throw new ServletException(
						"unable to create DocumentBuilderFactory", e);
			} catch (ServletException e1) {
				e1.printStackTrace();
			}
		}
		return docBuilder.newDocument();
	}

	private Document initDocument(HttpServletRequest request, Document doc,
			List dataList) {
		Document result = doc;
		Element wineResponse = doc.createElement("wineResponse");
		doc.appendChild(wineResponse);
		if (dataList != null) {
			String methodName = request.getParameter("method");
			Element methodElement = doc.createElement("method");
			methodElement.setAttribute("name", methodName);
			wineResponse.appendChild(methodElement);
			int i = 1;
			for (Iterator iter = dataList.iterator(); iter.hasNext();) {
				Map map = (Map) iter.next();
				Set key = map.keySet();
				Iterator it2 = key.iterator();
				Element data = doc.createElement("data");
				data.setAttribute("id", Integer.toString(i++));
				while (it2.hasNext()) {
					methodElement.appendChild(data);
					data.appendChild(newElementFromMap(map,
							(String) it2.next(), doc));
				}
			}
		} else {
			Element descriptionElement = doc.createElement("description");
			Text textNode = doc.createTextNode("No result set return");
			descriptionElement.appendChild(textNode);
			wineResponse.appendChild(descriptionElement);
		}
		return result;
	}

	private List<Map> initDataList(SQLHelper sqlHelper) throws Exception,
			MethodException {
		String sql = sqlHelper.getSQLStatement();
		if (sql == null)
			throw new MethodException(MethodErrorCode.SQL_STATEMENT_EMPTY);
		String token = new String();

		// initialize parameter statement
		StringBuffer tokenBuffer = new StringBuffer();
		Iterator tokenIt = sqlHelper.getToken().iterator();
		while (tokenIt.hasNext()) {
			String p = tokenIt.next().toString();
			tokenBuffer.append(p + " ");
		}
		if (tokenBuffer.length() - 1 > 0)
			token = tokenBuffer.substring(0, tokenBuffer.length() - 1);
		// System.out.println("token = " + token);

		// assert sql statement is insert/select/update/delete
		StringTokenizer stringToken = new StringTokenizer(sql);
		String first = new String();
		while (stringToken.hasMoreTokens()) {
			first = stringToken.nextToken();
			break;
		}

		// initialize List<Map> list
		List<Map> list = null;
		first = first.toLowerCase();
		if (first.equals("select")) {
			list = ReadData.readTable(sql, token);
		} else if (first.equals("insert") || first.equals("delete")
				|| first.equals("update")) {
			list = ReadData.updateTable(sql, token);
		} else {
			throw new MethodException(MethodErrorCode.SQL_RESTRICTION);
		}
		return list;
	}

	private Element newElementFromMap(Map map, String key, Document doc) {
		String text = map.get(key).toString();
		Text textNode = doc.createTextNode(text);
		Element element = doc.createElement(key);
		element.appendChild(textNode);
		return element;
	}
}
